package com.qms.repast.weidgets.adsorptionhelper;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.qms.repast.R;

public class HeaderViewPager extends LinearLayout {

	private static final int DIRECTION_UP = 1;
	private static final int DIRECTION_DOWN = 2;

	private int topOffset = 0;      //滚动的最大偏移量

	private Scroller mScroller;
	private int mTouchSlop;         //表示滑动的时候，手的移动要大于这个距离才开始移动控件。
	private int mMinimumVelocity;   //允许执行一个fling手势动作的最小速度值
	private int mMaximumVelocity;   //允许执行一个fling手势动作的最大速度值
	private int sysVersion;         //当前sdk版本，用于判断api版本
	private View mHeadView;         //需要被滑出的头部
	private int mHeadHeight;        //滑出头部的高度
	private int maxY = 0;           //最大滑出的距离，等于 mHeadHeight
	private int minY = 0;           //最小的距离， 头部在最顶部
	private int mCurY;              //当前已经滚动的距离
	private VelocityTracker mVelocityTracker;
	private int mDirection;
	private int mLastScrollerY;
	private boolean mDisallowIntercept;  //是否允许拦截事件
	private boolean isClickHead;         //当前点击区域是否在头部
	private OnScrollListener onScrollListener;   //滚动的监听
	private HeaderScrollHelper mScrollable;

	public interface OnScrollListener {
		void onScroll (int currentY, int maxY);
	}

	public void setOnScrollListener (OnScrollListener onScrollListener) {
		this.onScrollListener = onScrollListener;
	}

	public HeaderViewPager (Context context) {
		this(context, null);
	}

	public HeaderViewPager (Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public HeaderViewPager (Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);

		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HeaderViewPager);
		topOffset = a.getDimensionPixelSize(a.getIndex(R.styleable.HeaderViewPager_hvp_topOffset), topOffset);
		a.recycle();

		mScroller = new Scroller(context);
		mScrollable = new HeaderScrollHelper();
		ViewConfiguration configuration = ViewConfiguration.get(context);
		mTouchSlop = configuration.getScaledTouchSlop();   //表示滑动的时候，手的移动要大于这个距离才开始移动控件。
		mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); //允许执行一个fling手势动作的最小速度值
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); //允许执行一个fling手势动作的最大速度值
		sysVersion = Build.VERSION.SDK_INT;
	}

	@Override
	protected void onFinishInflate () {
		super.onFinishInflate();
		if (mHeadView != null && !mHeadView.isClickable()) {
			mHeadView.setClickable(true);
		}
	}

	@Override
	protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
		mHeadView = getChildAt(0);
		measureChildWithMargins(mHeadView, widthMeasureSpec, 0, MeasureSpec.UNSPECIFIED, 0);
		mHeadHeight = mHeadView.getMeasuredHeight();
		maxY = mHeadHeight - topOffset;
		//让测量高度加上头部的高度
		super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) + maxY, MeasureSpec.EXACTLY));
	}

	/**
	 * @param disallowIntercept 作用同 requestDisallowInterceptTouchEvent
	 */
	public void requestHeaderViewPagerDisallowInterceptTouchEvent (boolean disallowIntercept) {
		super.requestDisallowInterceptTouchEvent(disallowIntercept);
		mDisallowIntercept = disallowIntercept;
	}

	private float mDownX;  //第一次按下的x坐标
	private float mDownY;  //第一次按下的y坐标
	private float mLastY;  //最后一次移动的Y坐标
	private boolean verticalScrollFlag = false;   //是否允许垂直滚动

	/**
	 * 说明：一旦dispatTouchEvent返回true，即表示当前View就是事件传递需要的 targetView，事件不会再传递给
	 * 其他View，如果需要将事件继续传递给子View，可以手动传递 由于dispatchTouchEvent处理事件的优先级高于子View，也高于onTouchEvent,所以在这里进行处理
	 * 好处一：当有子View，并且子View可以获取焦点的时候，子View的onTouchEvent会优先处理，如果当前逻辑 在onTouchEnent中，则事件无法到达，逻辑失效
	 * 好处二：当子View是拥有滑动事件时，例如ListView，ScrollView等，不需要对子View的事件进行拦截，可以 全部让该父控件处理，在需要的地方手动将事件传递给子View，保证滑动的流畅性，结尾两行代码就是证明：
	 * super.dispatchTouchEvent(ev); return true;
	 */
	@Override
	public boolean dispatchTouchEvent (MotionEvent ev) {
		float currentX = ev.getX();                   //当前手指相对于当前view的X坐标
		float currentY = ev.getY();                   //当前手指相对于当前view的Y坐标
		float shiftX = Math.abs(currentX - mDownX);   //当前触摸位置与第一次按下位置的X偏移量
		float shiftY = Math.abs(currentY - mDownY);   //当前触摸位置与第一次按下位置的Y偏移量
		float deltaY;                                 //滑动的偏移量，即连续两次进入Move的偏移量
		obtainVelocityTracker(ev);                    //初始化速度追踪器
		switch (ev.getAction()) {
			//Down事件主要初始化变量
			case MotionEvent.ACTION_DOWN:
				mDisallowIntercept = false;
				verticalScrollFlag = false;
				mDownX = currentX;
				mDownY = currentY;
				mLastY = currentY;
				checkIsClickHead((int) currentY, mHeadHeight, getScrollY());
				mScroller.abortAnimation();
				break;
			case MotionEvent.ACTION_MOVE:
				if (mDisallowIntercept) {
					break;
				}
				deltaY = mLastY - currentY; //连续两次进入move的偏移量
				mLastY = currentY;
				if (shiftX > mTouchSlop && shiftX > shiftY) {
					//水平滑动
					verticalScrollFlag = false;
				} else if (shiftY > mTouchSlop && shiftY > shiftX) {
					//垂直滑动
					verticalScrollFlag = true;
				}
				/**
				 * 这里要注意，对于垂直滑动来说，给出以下三个条件
				 * 头部没有固定，允许滑动的View处于第一条可见，当前按下的点在头部区域
				 * 三个条件满足一个即表示需要滚动当前布局，否者不处理，将事件交给子View去处理
				 */
				if (verticalScrollFlag && (!isStickied() || mScrollable.isTop() || isClickHead)) {
					//如果是向下滑，则deltaY小于0，对于scrollBy来说
					//正值为向上和向左滑，负值为向下和向右滑，这里要注意
					scrollBy(0, (int) (deltaY + 0.5));
					invalidate();
				}
				break;
			case MotionEvent.ACTION_UP:
				if (verticalScrollFlag) {
					mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); //1000表示单位，每1000毫秒允许滑过的最大距离是mMaximumVelocity
					float yVelocity = mVelocityTracker.getYVelocity();  //获取当前的滑动速度
					mDirection = yVelocity > 0 ? DIRECTION_DOWN : DIRECTION_UP;  //下滑速度大于0，上滑速度小于0
					//根据当前的速度和初始化参数，将滑动的惯性初始化到当前View，至于是否滑动当前View，取决于computeScroll中计算的值
					//这里不判断最小速度，确保computeScroll一定至少执行一次
					mScroller.fling(0, getScrollY(), 0, -(int) yVelocity, 0, 0, -Integer.MAX_VALUE, Integer.MAX_VALUE);
					mLastScrollerY = getScrollY();
					invalidate();  //更新界面，该行代码会导致computeScroll中的代码执行
					//阻止快读滑动的时候点击事件的发生，滑动的时候，将Up事件改为Cancel就不会发生点击了
					if ((shiftX > mTouchSlop || shiftY > mTouchSlop)) {
						if (isClickHead || !isStickied()) {
							int action = ev.getAction();
							ev.setAction(MotionEvent.ACTION_CANCEL);
							boolean dd = super.dispatchTouchEvent(ev);
							ev.setAction(action);
							return dd;
						}
					}
				}
				recycleVelocityTracker();
				break;
			case MotionEvent.ACTION_CANCEL:
				recycleVelocityTracker();
				break;
			default:
				break;
		}
		//手动将事件传递给子View，让子View自己去处理事件
		super.dispatchTouchEvent(ev);
		//消费事件，返回True表示当前View需要消费事件，就是事件的TargetView
		return true;
	}

	private void checkIsClickHead (int downY, int headHeight, int scrollY) {
		isClickHead = ((downY + scrollY) <= headHeight);
	}

	private void obtainVelocityTracker (MotionEvent event) {
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);
	}

	private void recycleVelocityTracker () {
		if (mVelocityTracker != null) {
			mVelocityTracker.recycle();
			mVelocityTracker = null;
		}
	}

	@Override
	public void computeScroll () {
		if (mScroller.computeScrollOffset()) {
			final int currY = mScroller.getCurrY();
			if (mDirection == DIRECTION_UP) {
				// 手势向上划
				if (isStickied()) {
					//这里主要是将快速滚动时的速度对接起来，让布局看起来滚动连贯
					int distance = mScroller.getFinalY() - currY;    //除去布局滚动消耗的时间后，剩余的时间
					int duration = calcDuration(mScroller.getDuration(), mScroller.timePassed()); //除去布局滚动的距离后，剩余的距离
					mScrollable.smoothScrollBy(getScrollerVelocity(distance, duration), distance, duration);
					//外层布局已经滚动到指定位置，不需要继续滚动了
					mScroller.abortAnimation();
					return;
				} else {
					scrollTo(0, currY);  //将外层布局滚动到指定位置
					invalidate();        //移动完后刷新界面
				}
			} else {
				// 手势向下划，内部View已经滚动到顶了，需要滚动外层的View
				if (mScrollable.isTop() || isClickHead) {
					int deltaY = (currY - mLastScrollerY);
					int toY = getScrollY() + deltaY;
					scrollTo(0, toY);
					if (mCurY <= minY) {
						mScroller.abortAnimation();
						return;
					}
				}
				//向下滑动时，初始状态可能不在顶部，所以要一直重绘，让computeScroll一直调用
				//确保代码能进入上面的if判断
				invalidate();
			}
			mLastScrollerY = currY;
		}
	}

	@SuppressLint("NewApi")
	private int getScrollerVelocity (int distance, int duration) {
		if (mScroller == null) {
			return 0;
		} else if (sysVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
			return (int) mScroller.getCurrVelocity();
		} else {
			return distance / duration;
		}
	}

	/**
	 * 对滑动范围做限制
	 */
	@Override
	public void scrollBy (int x, int y) {
		int scrollY = getScrollY();
		int toY = scrollY + y;
		if (toY >= maxY) {
			toY = maxY;
		} else if (toY <= minY) {
			toY = minY;
		}
		y = toY - scrollY;
		super.scrollBy(x, y);
	}

	/**
	 * 对滑动范围做限制
	 */
	@Override
	public void scrollTo (int x, int y) {
		if (y >= maxY) {
			y = maxY;
		} else if (y <= minY) {
			y = minY;
		}
		mCurY = y;
		if (onScrollListener != null) {
			onScrollListener.onScroll(y, maxY);
		}
		super.scrollTo(x, y);
	}

	/**
	 * 头部是否已经固定
	 */
	public boolean isStickied () {
		return mCurY == maxY;
	}

	private int calcDuration (int duration, int timepass) {
		return duration - timepass;
	}

	public int getMaxY () {
		return maxY;
	}

	public boolean isHeadTop () {
		return mCurY == minY;
	}

	/**
	 * 是否允许下拉，与PTR结合使用
	 */
	public boolean canPtr () {
		return verticalScrollFlag && mCurY == minY && mScrollable.isTop();
	}

	public void setTopOffset (int topOffset) {
		this.topOffset = topOffset;
	}

	public void setCurrentScrollableContainer (HeaderScrollHelper.ScrollableContainer scrollableContainer) {
		mScrollable.setCurrentScrollableContainer(scrollableContainer);
	}
}